<?php
// +----------------------------------------------------------------------
// | A3Mall
// +----------------------------------------------------------------------
// | Copyright (c) 2020 http://www.a3-mall.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: xzncit <158373108@qq.com>
// +----------------------------------------------------------------------

namespace app\common\model\order;

use app\common\model\base\Base;
use app\common\model\file\Association;
use app\common\model\job\Job;
use app\common\model\users\Wallet;
use app\common\model\users\WalletDetail;
use app\common\traits\recordAllRun;
use mall\basic\Users;
use think\facade\Db;

class OrderTake extends Base
{
    use recordAllRun;
    //订单状态常量

    //已接单
    public const STATUS_ACCEPT=1;
    //已完成待确认
    public const STATUS_WAIT_CONFIRM=2;
    //已完成不通过
    public const STATUS_FAILED=3;
    //已完成维权中
    public const STATUS_APPEAL=4;
    //已完成
    public const STATUS_FINISH=5;


    /**
     * 关联文件类型
     */
    const FILE_TYPE = 'jobtake';

    //状态文本映射
    private static $status_text_mapping=[
        self::STATUS_ACCEPT=>'已接单',
        self::STATUS_WAIT_CONFIRM=>'已完成待确认',
        self::STATUS_FAILED=>'已完成不通过',
        self::STATUS_APPEAL=>'已完成维权中',
        self::STATUS_FINISH=>'已完成',

    ];

    //获取状态文本
    public static function getStatusText(int $status){
        return self::$status_text_mapping[$status] ?? '';
    }

    //获取状态列表
    public static function getStatusList()
    {
        return self::$status_text_mapping;
    }

    public static function isAppeal(int $status)
    {
        return $status==self::STATUS_APPEAL;
    }


    public static function isFailed(int $status)
    {
        return $status==self::STATUS_FAILED;
    }

    public static function isWaitConfirm(int $status)
    {
        return $status==self::STATUS_WAIT_CONFIRM;
    }

    public static function isFinish(int $status)
    {
        return $status==self::STATUS_FINISH;
    }

    public static function isAccept(int $status)
    {
        return $status==self::STATUS_ACCEPT;
    }


    /**
     * 关联任务数据
     */
    public function job() {
        return $this->hasOne(Job::class,"id","job_id")->joinType("INNER");
    }

    /**
     * 关联获取图片详情
     * @return \think\model\relation\HasMany
     */
    public function images() {
        return $this->hasMany(Association::class,'association_id','id')
            ->where('association_type','=',Association::ASSOCIATION_TYPE_LIST[static::FILE_TYPE])->where('status','=',Association::STATUS_EFFECTIVE)
            ->field('filename');
    }

    /**
     * 关联获取图片详情
     * @return \think\model\relation\HasMany
     */
    public function users() {
        return $this->hasOne(\app\common\model\users\Users::class,"id","user_id")->bind(['username'])->joinType("INNER");
    }


    //获取状态文本
    // public function getStatusTextAttr($value,$data)
    // {
    //     return self::$status_text_mapping[$data['status']];
    // }

    // public function getCreateTimeTextAttr($value,$data)
    // {
    //     return date('Y-m-d H:i:s',$data['create_time']);
    // }

    // public function getSubmitTimeTextAttr($value,$data)
    // {
    //     return date('Y-m-d H:i:s',$data['submit_time']);
    // }

    /**
     * 新增接单任务
     * @param $jobDetails
     * @return int|string
     * @throws \Exception
     */
    public function accept($jobDetails)
    {
        // 获取用户ID
        $userId = Users::get('id');

        /****** 判断是否存在 *******/
        if($this->where([
            'user_id'   => $userId,
            'job_id'    => $jobDetails['id']
        ])->count()) {
            throw new \Exception('该任务您已接过，请勿重复接单');
        }
        /**** END 判断是否存在 *****/

        $time = time();
        $insertData = [
            'order_no'  => $this->getOrderNo($time),
            'job_id'    => $jobDetails['id'],
            'job_no'    => $jobDetails['job_no'],
            'user_id'   => $userId,
            'status'    => static::STATUS_ACCEPT,
            'create_time'   => $time,
            'submit_time'   => 0,
            'update_time'   => $time,
            'ver'   => 1,
            'wait_expire'   => $this->getSubmitExpire($time)
        ];
        return $this->insert($insertData);
    }


    /**
     * 提交接单任务
     * @param $orderTakeDetails 接单任务详情
     * @param $data 提交的参数
     * @return bool
     * @throws \Exception
     */
    public function submit($orderTakeDetails,$data) {
        $time = time();
        $updateData = [
            'status'    => static::STATUS_WAIT_CONFIRM,
            'wait_expire'      => $this->getReviewExpire($time),  //添加审批期限
            'submit_time'   => $time,
        ];
        $this->updateByIdAndVer($orderTakeDetails['id'],$orderTakeDetails['ver'],$updateData);
        // 添加图片
        (new Association())->recordInsertListByOrderTake($orderTakeDetails['id'],$data['verify_imgs']);
        return true;
    }

    /**
     * 审批失败
     * @param $orderTakeDetails 接单详情
     * @param $data 提交数据
     */
    public function failed($orderTakeDetails,$data) {
        $time = time();
        $updateData = [
            'failure_reason'    => $data['failure_reason'],
            'wait_expire' => $this->getAppealExpire($time),    //获取维权期限
            'status'    => static::STATUS_FAILED,
        ];
        $this->updateByIdAndVer($orderTakeDetails['id'],$orderTakeDetails['ver'],$updateData);
        return true;
    }

    /**
     * 申请维权
     * @param $orderTakeDetails 接单详情
     */
    public function appeal($orderTakeDetails) {
        $time = time();
        $updateData = [
            'appeal_time'   => $time,
            'status'    => static::STATUS_APPEAL,
        ];
        $this->updateByIdAndVer($orderTakeDetails['id'],$orderTakeDetails['ver'],$updateData);
        return true;
    }

    /**
     * 验证成功操作
     * @param $orderTakeDetails 接单详情
     */
    public function success($orderTakeDetails) {
        // 接单完成
        $this->close($orderTakeDetails['id'],$orderTakeDetails['ver'],$orderTakeDetails['job-id'],$orderTakeDetails['job-ver'],$orderTakeDetails['user_id'],$orderTakeDetails['job-price']);
        return true;
    }


    /**
     * 结束接单任务
     * @param $id 接单任务ID
     * @param $version 接单任务版本
     * @param $jobId 任务ID
     * @param $jobVer 任务版本
     * @param $userId 接单人
     * @param $amount 接单人获得金额
     * @throws \Exception
     */
    public function close($id,$version,$jobId,$jobVer,$userId,$amount) {
        $updateData = [
            'status'    => bccomp($amount,0,2) == 1?static::STATUS_FINISH:static::STATUS_FAILED,
            'is_closed' => 1,
        ];
        // 设置当前状态
        $this->updateByIdAndVer($id,$version,$updateData);

        if(static::isFinish($updateData['status'])) { // 进账计算
            if(!$walletDetails = (new Wallet())->findOne($userId)) {
                throw new \Exception('系统繁忙，请稍后再试');
            }
            (new WalletDetail())->recordAddToWallet($walletDetails,$amount,'acceptIn',[
               'related_id' => $id,
               'related_pid'    => $jobId
            ]);
        }

        // 任务关闭关闭接单任务
        (new Job())->orderTakeClose($jobId,$jobVer,static::isFinish($updateData['status']));

        return true;
    }



    /**
     * 检测并关闭接单任务
     * @param bool $startTrans 处理时是否单个开启事务
     * @param int $limit 每次执行的最大任务数
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function closeCheck($startTrans = true,$limit = 500) {
        $result = [ //返回的信息
            'num'   => 0,
            'success'   => 0,
            'failed'    => 0,
            'failedMsgList' => [],  //执行错误的ID列表
        ];
        // 获取需要结束的接单信息
        if(!$closeList = $this->recordAll( [
            ['is_closed','=',0],
            ['wait_expire','<',time()],
            ['status','<>',static::STATUS_APPEAL]
        ],[0,$limit],[],['job-ver','job-price'])) {
            return $closeList;
        }

        // 执行结束函数
        $closeFunc = function ($details) {
            $amount = static::isWaitConfirm($details['status'])?$details['job-price']:0;
            $this->close($details['id'],$details['ver'],$details['job_id'],$details['job-ver'],$details['user_id'],$amount);
        };
        /**
         * 循环处理结单
         */
        foreach($closeList as $orderDetails) {
            if($startTrans) {
                Db::transaction(function () use(&$result,$orderDetails,$closeFunc){
                    $result['num']++;
                    try {
                        $closeFunc($orderDetails);
                        $result['success']++;
                    } catch (\Exception $e) {
                        $result['failed']++;
                        $result['failedMsgList'][$orderDetails['id']] = $e->getMessage();
                    }
                });
            } else {
                $closeFunc($orderDetails);
                $result['num']++;
                $result['success']++;
            }
        }
        return $result;
    }

    /**
     * 通过 ID 获取接单记录详情
     * @param $id
     * @param null $userId
     * @return array|null|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function findById($id,$userId = null){
        $where = [
            'id' => $id,
        ];
        if(!is_null($userId)) {
            $where['user_id'] = $userId;
        }
        // 查询并返回结果
        return $this->where($where)->find();
    }

    /**
     * 获取关联详情
     * @param $id 接单ID
     * @param $userId   用户名
     * @param null $join 关联的表
     * @return array|null|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function findByIdAndJoinDetails($id,$userId,$join = null) {
        $join = is_array($join)?$join:[];

        $return = [];
        if(!$details = $this->findById($id,$userId)) {
            return $details;
        }
        // 转换
        $return = $details->toArray();
        foreach($join as $joinTable) {
            $tmp = null;
            switch($joinTable) {
                case 'images':
                    if(is_null($tmp)) {
                        $tmp = $details->images;
                    }
                case 'job-images':
                    if(is_null($tmp)) {
                        $tmp = $details->job->images;
                    }
                    $return[$joinTable] = array_column($tmp->toArray(),'filename');
                    break;
                case 'users':
                    if(is_null($tmp)) {
                        $tmp = $details->users;
                    }
                case 'job':
                    if(is_null($tmp)) {
                        $tmp = $details->job;
                    }
                case 'job-requirement':
                    if(is_null($tmp)) {
                        $tmp = $details->job->requirement;
                    }
                case 'job-publisher':
                    if(is_null($tmp)) {
                        $tmp = $details->job->publisher;
                    }
                    $tmp = $tmp->toArray();
                    foreach($tmp as $key=>$val) {
                       $return[$joinTable . '-' . $key] = $val;
                    }
                    break;
            }
        }
        return $return;
    }


    /**
     * 获取提交期限
     * @param null $time
     */
    private function getSubmitExpire($time = null) {
        $time = is_null($time)?time():$time;
        return $time + 60 * 30;
    }

    /**
     * 获取审批期限
     * @param null $time
     */
    private function getReviewExpire($time = null) {
        $time = is_null($time)?time():$time;
        return $time +  60 * 60 * 24 * 2;
    }

    /**
     * 获取提交期限
     * @param null $time
     */
    private function getAppealExpire($time = null) {
        $time = is_null($time)?time():$time;
        return $time + 60 * 60 * 24;
    }



    /**
     * 获取订单号
     * @param null $time
     * @return string
     */
    private function getOrderNo($time = null){
        return date('YmdHis',empty($time)?time():$time) . rand(10000,99999);
    }


    /**
     * 通过ID和版本号更新数据
     * @param $userId
     * @param $version
     * @param $updateData
     */
    private function updateByIdAndVer($id,$version,$updateData)
    {

        $updateData['ver'] = Db::raw('ver+1');
        $updateData['update_time'] = time();
        if(!$this->where([
            'id'   => $id,
            'ver'   => $version,
        ])->update($updateData)) {
            throw new \Exception('数据发生变化，请刷新重试');
        }
        return true;
    }

    /** 获取关联所有数据的列表
     * @param array $whereTmp  条件
     * @param array $limit  查询数量
     * @param string $order 排序
     * @param array $fields 查询字段（暂缺）
     * @return array
     */
    public function recordAll($whereTmp = [],$limit = [],$orderTmp = [],$fieldsTmp= []) {

        return $this->recordAllRun([$whereTmp,$limit,$orderTmp,$fieldsTmp],[],[
            'job'      => [Job::getTable(),'job.id=' . static::getTable() . '.job_id','INNER'],
            'users'      => [\app\common\model\users\Users::getTable(),'users.id=' . static::getTable() . '.user_id','INNER'],
            'job_users'      => [\app\common\model\users\Users::getTable(),'job_users.id=job.publisher_id','INNER'],
            'income'      => [WalletDetail::getTable(),'income.related_id=' . static::getTable() . '.id AND income.type=' . WalletDetail::TYPE_LIST['acceptIn'][0],'LEFT'],
        ]);
    }

    public function getList($condition,$size=10,$page=1){
        $count = $this->withJoin(["users"])->where($condition)->count();
        $data = $this->withJoin(["users","job"])->where($condition)->order('order_take.id','DESC')->paginate($size);

        return [
            "count"=>$count,
            "data"=>array_map([$this,'format'],$data->items())
        ];
    }


    public function getStatusTextAttr($value,$data)
    {
        return static::getStatusText($data['status']) ?? '';
    }

   public function getSubmitTimeAttr($value){
       return date("Y-m-d H:i:s",$value);
   }

   public function getAppealTimeAttr($value){
       return date("Y-m-d H:i:s",$value);
   }

    protected function format($value)
    {
        $value->status_name=$value->status_text;
        return $value;
    }

}
